-
Notifications
You must be signed in to change notification settings - Fork 206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(localchain): add transfer
method to LocalChainAccountKit holder
#9380
Conversation
Deploying agoric-sdk with Cloudflare Pages
|
b9e751c
to
da20cb6
Compare
9a89435
to
ee97149
Compare
@@ -1,11 +1,30 @@ | |||
// @ts-checck |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might consider renaming this - it's meant to capture the OrchestrationAccount
interface implemented for a LocalChainAccount
.
|
||
// TODO #8879 chainInfo and #9063 well-known chains | ||
const { transferChannel } = | ||
this.state.agoricChainInfo.connections.get(destination.chainId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The destination: ChainAddress
argument seems to assume we should be able to get channelId
s from a chainId
}), | ||
'can create transfer msg with memo', | ||
); | ||
// TODO, can we intercept the bridge message to see that it has a memo? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @turadg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah. I could add that to #9396 but I wouldn't have a case to drive it. I'd prefer to sequence that first and add the functionality here. Consider rebasing this branch on top of 9207-bridge
agoricChainInfo, | ||
}); | ||
} | ||
|
||
const publicFacet = zone.exo('StakeBld', undefined, { | ||
makeStakeBldInvitation() { | ||
return zcf.makeInvitation( | ||
async seat => { | ||
const { give } = seat.getProposal(); | ||
trace('makeStakeBldInvitation', give); | ||
// XXX type appears local but it's remote |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment was for E(privateArgs.localchain).makeAccount()
. best to remove it at this point
undefined, | ||
undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
omission has the same effect
undefined, | |
undefined, |
* @param {TimerService} initState.timer | ||
* @param {TimerBrand} initState.timerBrand | ||
* @param {AgoricChainInfo} initState.agoricChainInfo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these aren't specific to each account kit, right? they can stay in the prepareLocalChainAccountKit
closure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call out, thank you
memo: opts?.memo ?? '', | ||
}), | ||
]); | ||
void trace('MsgTransfer result', result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
trace
is sync.
void trace('MsgTransfer result', result); | |
trace('MsgTransfer result', result); |
* @returns void | ||
* | ||
* TODO document the mapping from the address to the destination chain. | ||
*/ | ||
transfer: ( | ||
amount: AmountArg, | ||
destination: ChainAddress, | ||
memo?: string, | ||
opts?: IBCMsgTransferOptions, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dtribble wants to review changes to orchestration-api
. These look to me like fixups of the existing API so I wouldn't block awaitinghis review
type StartFn = | ||
typeof import('@agoric/orchestration/src/examples/stakeBld.contract.js').start; | ||
|
||
test('stakeBld contract - makeAccount, deposit, withdraw', async t => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks I missed this one when adding tests of the examples
}), | ||
'can create transfer msg with memo', | ||
); | ||
// TODO, can we intercept the bridge message to see that it has a memo? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah. I could add that to #9396 but I wouldn't have a case to drive it. I'd prefer to sequence that first and add the functionality here. Consider rebasing this branch on top of 9207-bridge
/** | ||
* make a new durable baggage and zone for each vat | ||
*/ | ||
export const makeBaggageAndZone = () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
odd to have both. consider makeRootZone
and make baggage from it as needed
rootZone.mapStore('publisher')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sharing thoughts so far, before going to a meeting
versions: { identifier: string; features: string[] }[]; | ||
delay_period: bigint; | ||
}; | ||
connections: MapStore<string, IBCConnectionInfo>; // chainId or wellKnownName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mutable... hm... 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would you expect to see instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure... it's an adjustment to my mental model, and I'm not sure where I might have assumed that ChainInfo is immutable. I just need to be aware of it going forward, I suppose.
(technically, this still is immutable; the value of the connections
property doesn't change. it's always the same MapStore. It's just the contents of that MapStore that may change.)
One impact is the contents of connections
aren't included when a CosmosChainInfo
is marshaled to vstorage as part of agoricNames.
const timerService = await timerServiceP; | ||
const timerBrand = await E(timerService).getTimerBrand(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's an unnecessary round-trip here. I wonder whether it's worth using Promise.all()
to remove it.
@@ -37,6 +46,8 @@ export const startStakeBld = async ({ | |||
terms: {}, | |||
privateArgs: { | |||
localchain: await localchain, | |||
timerBrand, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a little odd to pass timerBrand
in separately. The contract has to rely on the caller to make sure it corresponds to timerService
. Well, I suppose contracts do rely on their caller in general. So it's not a serious problem. It just feels weird.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was looking to cache the brand since it's a remote call. I can do this in the start function instead.
* @param {RelativeTimeRecord} [relativeTime] defaults to 5 minutes | ||
* @returns {Promise<bigint>} Timeout timestamp in absolute nanoseconds since unix epoch | ||
*/ | ||
async getCurrentTimestamp(relativeTime) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not use the same name (getCurrentTimestamp
) for a different behavior.
Perhaps getTimeoutNS()
?
*/ | ||
async getCurrentTimestamp(relativeTime) { | ||
const currentTime = await E(timer).getCurrentTimestamp(); | ||
return ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little surprised the linter didn't remove the outer parens.
@@ -29,25 +32,34 @@ export type CosmosValidatorAddress = ChainAddress & { | |||
addressEncoding: 'bech32'; | |||
}; | |||
|
|||
/** Info for an IBC Connection (Chain:Chain relationship, that can contain multiple Channels) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/** Info for an IBC Connection (Chain:Chain relationship, that can contain multiple Channels) */ | |
/** Info for an IBC Connection (Chain:Chain relationship that can contain multiple Channels) */ |
A restrictive clause is not offset with commas. -- Restrictive Clause: Explanation and Examples
or change "that" to "which" for a non-restrictive clause:
/** Info for an IBC Connection (Chain:Chain relationship, that can contain multiple Channels) */ | |
/** Info for an IBC Connection (Chain:Chain relationship, which can contain multiple Channels) */ |
I'm cursed with a brain that throws syntax errors on English grammar just as much as programming language grammar. :-/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you - I appreciate your appreciation of grammar!
export type IBCConnectionInfo = { | ||
id: string; // e.g. connection-0 | ||
client_id: string; // '07-tendermint-0' | ||
state: 'OPEN' | 'TRYOPEN' | 'INIT' | 'CLOSED'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that on-the-wire it's STATE_OPEN
, not OPEN
. Did we diverge on purpose?
orthogonal to this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oversight, these should all be prefixed with STATE_
, and CLOSED
doesn't exist. But yes orthogonal.
Also seeing a StateSDKType
type in @agoric/cosmic-proto/ibc/core/connection/v1/connection.js
we can use instead. (And a similar one in /ibc/core/channel/v1/channel.js
)
versions: { identifier: string; features: string[] }[]; | ||
delay_period: bigint; | ||
}; | ||
connections: MapStore<string, IBCConnectionInfo>; // chainId or wellKnownName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when do we use chainId
vs wellKnownName
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question, I am also wondering the same. The transfer
parameters imply we should be able to get there from chainId
or chainId -> wellKnownName
. https://github.com/Agoric/agoric-sdk/pull/9380/files#r1610510589
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think solving this can wait for #9063
* @param {import('@agoric/vats/src/localchain.js').LocalChainAccount} account | ||
* @param {StorageNode} storageNode | ||
* @param {object} initState | ||
* @param {LocalChainAccount} initState.account |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need a LocalChainAccount
to make a LocalchainAccountKit
? That hurts my brain. Usually, we have either makeX
or makeXKit
, not both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree this doesn't make sense. I suggested we rename this to something closer to OrchestrationAccountKit
here:
https://github.com/Agoric/agoric-sdk/pull/9380/files#r1610504823
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that makes some sense. Though will OrchestrationAccountKit
be the thing for all OrchestratrionAccounts?
Another option is LocalChainAccountHolder
, since we will be wrapping the LCA object in an Ownable holder for rights transfer. #9087
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Though will OrchestrationAccountKit be the thing for all OrchestratrionAccounts
I'm not sure the best approach here yet, I suppose there are multiple.
I did not end up tackling this change here -seems like it can be picked up as part of #9212
const result = await E(lca).executeTx([ | ||
const [result] = await E(lca).executeTx([ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wild... was there a bug here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The expected return for delegate()
is Promise<void>
, so no. In the next pass I can change the return statement of this function.
ee97149
to
a039d56
Compare
versions: { identifier: string; features: string[] }[]; | ||
delay_period: bigint; | ||
}; | ||
connections: MapStore<string, IBCConnectionInfo>; // chainId or wellKnownName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think solving this can wait for #9063
@@ -1,6 +1,8 @@ | |||
/** | |||
* @file Example contract that uses orchestration | |||
*/ | |||
// @ts-check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the tsconfig already has checkJs: true
. Please keep @ts-check
out of this package so it doesn't look like it's necessary.
const timerBrand = await E(privateArgs.timerService).getTimerBrand(); | ||
const timestampHelper = makeTimestampHelper( | ||
privateArgs.timerService, | ||
timerBrand, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see timerBrand used here. Consider,
const timerBrand = await E(privateArgs.timerService).getTimerBrand(); | |
const timestampHelper = makeTimestampHelper( | |
privateArgs.timerService, | |
timerBrand, | |
); | |
const timestampHelper = await makeTimestampHelper( | |
privateArgs.timerService | |
); |
Or if you expect to need timeBrand in the future,
const timerBrand = await E(privateArgs.timerService).getTimerBrand(); | |
const timestampHelper = makeTimestampHelper( | |
privateArgs.timerService, | |
timerBrand, | |
); | |
const { timerBrand, timestampHelper } = await makeTimestampHelper( | |
privateArgs.timerService | |
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see timerBrand used here.
It's used in makeTimestampHelper
on line 31 to build the default, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makeTimestampHelper
, taking the timerService, can get the brand itself. That's why my examples change it to async
makeAcountInvitationMaker: M.call().returns(M.promise()), | ||
makeStakeBldInvitation: M.call().returns(M.promise()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this way it'll validate the return value. It at least improves the documentation. We should use this style so when we start inferring TS types from the Pattern the info is there.
makeAcountInvitationMaker: M.call().returns(M.promise()), | |
makeStakeBldInvitation: M.call().returns(M.promise()), | |
makeAcountInvitationMaker: M.callWhen().returns(InvitationShape), | |
makeStakeBldInvitation: M.callWhen().returns(InvitationShape), |
const lcaSeatKit = zcf.makeEmptySeatKit(); | ||
atomicTransfer(zcf, seat, lcaSeatKit.zcfSeat, give); | ||
seat.exit(); | ||
trace('makeStakeBldInvitation tryExit lca userSeat'); | ||
await E(lcaSeatKit.userSeat).tryExit(); | ||
trace('awaiting payouts'); | ||
const payouts = await E(lcaSeatKit.userSeat).getPayouts(); | ||
trace('awaiting deposit'); | ||
await E(holder).deposit(await payouts.In); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think https://github.com/Agoric/agoric-sdk/pull/9376/files#diff-54fb24f937c4f534bd01703bb857887a4a8e88ff34c8ba0948694bd69c22e375R85 may be a better pattern. cc @dckc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh it's in master now
// deposit funds from user seat to LocalChainAccount | |
const payments = await withdrawFromSeat(zcf, seat, give); | |
await deeplyFulfilled(objectMap(payments, localAccount.deposit)); | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to changes in this PR but happy to include
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, these lines are +
but they were moved. Please if you don't mind
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went with a sightly different approach:
const { In } = await deeplyFulfilled(
withdrawFromSeat(zcf, seat, give),
);
await E(holder).deposit(In);
Saw this with objectMap
:
ℹ RemoteError(error:captp:far-zoeTest#20001)#5: Method "In \"deposit\" method of (Account Holder holder)" called without 'this' object
ℹ Error: Method "In \"deposit\" method of (Account Holder holder)" called without 'this' object
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if that's the one payment then that works. The other is more general and I suspect we'll want a utility for it. Something to look into near the end as we clean up the DX
* @param {import('@agoric/vats/src/localchain.js').LocalChainAccount} account | ||
* @param {StorageNode} storageNode | ||
* @param {object} initState | ||
* @param {LocalChainAccount} initState.account |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that makes some sense. Though will OrchestrationAccountKit
be the thing for all OrchestratrionAccounts?
Another option is LocalChainAccountHolder
, since we will be wrapping the LCA object in an Ownable holder for rights transfer. #9087
@@ -145,17 +145,17 @@ export interface OrchestrationAccountI { | |||
/** | |||
* Transfer an amount to another account, typically on another chain. | |||
* The promise settles when the transfer is complete. | |||
* @param amount - the amount to transfer. | |||
* @param amount - the amount to transfer. Can be provided as pure data using denoms or as native Amounts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"native" is ambiguous as to whether native to Agoric or the host chain.
* @param amount - the amount to transfer. Can be provided as pure data using denoms or as native Amounts. | |
* @param amount - the amount to transfer. Can be provided as pure data using denoms or as ERTP Amounts. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 updating the AmountArg
annotation as well
|
||
t.is( | ||
await getCurrentTimestamp( | ||
TimeMath.coerceRelativeTimeRecord(1n, timerBrand), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isn't this more general? why should time export the 'make' function?
packages/vats/tools/fake-bridge.js
Outdated
'simulated unexpected MsgTransfer packet timeout', | ||
); | ||
} | ||
// TODO, can we reference bank to ensure user has tokens they are transfering? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fee free to mention #9402
@@ -99,7 +101,8 @@ export const start = async (zcf, privateArgs, baggage) => { | |||
{ | |||
async makeAccount() { | |||
trace('makeAccount'); | |||
return makeAccount().then(({ account }) => account); | |||
const { account } = await makeAccount(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if account
is a property of the return value rather than the return value itself, the function should be called makeAccountKit
, not makeAccount
.
baggage, | ||
makeRecorderKit, | ||
zcf, | ||
timestampHelper, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that seems like an odd factoring. Is prepareLocalChainAccountKit
supposed to work with an arbitrary implementation of this interface? Doesn't seem like it. Passing the timerService
seems more straightforward.
Pass timerBrand
too if await
ing it inside prepareLocalChainAccountKit
would be a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree the caller of prepareLocalChainAccountKit
should only have to know about timerService
. See 68c8762. I also moved getTimerBrand()
back to the proposal to avoid an await during prepare.
); | ||
trace('awaiting deposit'); | ||
await E(account).deposit(await payouts.In); | ||
async function makeLocalAccount() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as noted earlier:
async function makeLocalAccount() { | |
async function makeLocalAccountKit() { |
cf docs on Method Naming Structure
sourceChannel: transferChannel.channelId, | ||
token: { | ||
amount: String(amount.value), | ||
// @ts-expect-error AmountArg already narrowed to DenomAmount |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you change the !matches(...) || Fail...
to if (typeof amount !== 'string') throw ...
, I think you won't need this override.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if ('brand' in amount)
makes it happy. in
, more precisely
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I frequently forget to do this instead of if (amount.brand)
. Hopefully after writing it out now, I won't 🙂
// TODO #9211 lookup denom from brand | ||
!matches(amount, AmountShape) || Fail`ERTP Amounts not yet supported`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what #9211 should add here. Looking up a denom for a brand in agoricNames.vbankAsset is straightforward.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair, really only comes into play for ICA accounts. Will still tackle this in a follow up.
@@ -0,0 +1,87 @@ | |||
/** | |||
* @file Mocked Chain Info object until #8879 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UNTIL is a handy notation, but I'm uneasy that there's little to stop us from closing issues without addressing them.
maybe a back-link to this PR will help a little:
* currently keyed by ChainId, as this is what we have | ||
* available in ChainAddress to determine the correct IBCChannelID's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
I'm more comfortable relying on chainid since learning that they're available in-protocol (client state).
I don't think the protocol guarantees uniqueness across the interchain, so when we query for client state, we'll have to be careful to ... um... do something if we find a collision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok cool. The only potential hiccup I see, if human readability of the identifier is a design constraint, is that evm chain ids are typically integers.
ordering: Order.ORDER_UNORDERED, | ||
state: IBCChannelState.STATE_OPEN, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if using these symoblic constants rather than literals was due to typing, you can address that by surrounding the whole object literal with a const
type cast.
* @param {Zone} zone | ||
* @returns {Pick<CosmosChainInfo, 'connections' | 'chainId'>} | ||
*/ | ||
export const prepareMockChainInfo = zone => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like odd factoring. Passing the mapstore seems more straightforward. Then you can call it addAllChainInfo
or the like.
await t.throwsAsync( | ||
() => E(account).transfer({ denom: 'ubld', value: 1n }, unknownDestination), | ||
{ | ||
message: /not found/, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test that the bad chainId (unknown
) is included in the message?
suggest: choose something more novel than unknown
as the bad value
E(account).transfer({ denom: 'ubld', value: 10n }, destination, { | ||
memo: 'hello', | ||
}), | ||
'can create transfer msg with memo', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder about unit tests for just building the msg. you had a file of message builders in cosgov. that might be handy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth exploring. With jest I would reach for something like toHaveBeenCalledWith for on toBridge
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use sinon to spy, but be careful with shared state
And it doesn't play well with SES and durability. When we need such capability we've been making fakes and putting in that instrumentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still have lots of suggestions to consider, but I suppose none of them is critical.
The implementation of the feature looks correct and the tests are sufficient.
|
||
// XXX is this safe to call before prepare statements are completed? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, it's a remote call and those can't happen in the first crank of upgrade. but we can tackle that when this gets an upgrade test
68c8762
to
5bae30c
Compare
- towards `OrchestrationAccount` interface for a `LocalChainAccount` - includes `mockChainInfo` as IBCChannelIDs are needed to construct a transfer message from the `ChainAddress` parameter
5bae30c
to
fd11145
Compare
refs: #9193
Description
Adds
.transfer()
method toLocalChainAccountKit
, working towardsOrchestrationAccountI['transfer']
interface.Security Considerations
Scaling Considerations
Documentation Considerations
Testing Considerations
Upgrade Considerations